/**
 * Issue domain class.
 */
class Issue implements Comparable {
    static transients = ['summary', 'issueType', 'issueStatus', 'issueResolution', 'issueSeverity', 'description', 'assignee']

    Integer number
    Project project
    Set<IssueLink> links
    SortedSet<IssueSummaryTimeline> summaryTimelines = new TreeSet();
    SortedSet<IssueTypeTimeline> typeTimelines = new TreeSet();
    SortedSet<IssueStatusTimeline> statusTimelines = new TreeSet();
    SortedSet<IssueResolutionTimeline> resolutionTimelines = new TreeSet();
    SortedSet<IssueSeverityTimeline> severityTimelines = new TreeSet();
    SortedSet<IssueDescriptionTimeline> descriptionTimelines = new TreeSet();
    SortedSet<IssueAssigneeTimeline> assigneeTimelines = new TreeSet();
    SortedSet<IssueComment> comments = new TreeSet();

    static belongsTo = [project: Project]

    static hasMany = [links: IssueLink, summaryTimelines: IssueSummaryTimeline, typeTimelines: IssueTypeTimeline,
            statusTimelines: IssueStatusTimeline, resolutionTimelines: IssueResolutionTimeline,
            severityTimelines: IssueSeverityTimeline, descriptionTimelines: IssueDescriptionTimeline,
            assigneeTimelines: IssueAssigneeTimeline, comments: IssueComment]

    static constraints = {
        number(nullable: true, unique: 'project')
        project(nullable: false)
    }

    static mappedBy = [links: 'from']

    public String getSummary() {
        return !summaryTimelines.empty ? summaryTimelines.last().summary : null
    }

    public void setSummary(String summary) {
        if (summary != getSummary()) {
            addToSummaryTimelines(new IssueSummaryTimeline(summary: summary))
        }
    }

    public IssueType getIssueType() {
        return !typeTimelines.empty ? typeTimelines.last().type : null
    }

    public void setIssueType(IssueType issueType) {
        if (issueType?.id != getIssueType()?.id) {
            addToTypeTimelines(new IssueTypeTimeline(type: issueType))
        }
    }

    public IssueStatus getIssueStatus() {
        return !statusTimelines.empty ? statusTimelines.last().status : null
    }

    public void setIssueStatus(IssueStatus issueStatus) {
        if (issueStatus?.id != getIssueStatus()?.id) {
            addToStatusTimelines(new IssueStatusTimeline(status: issueStatus))
        }
    }

    public IssueResolution getIssueResolution() {
        return !resolutionTimelines.empty ? resolutionTimelines.last().resolution : null
    }

    public void setIssueResolution(IssueResolution issueResolution) {
        if (issueResolution?.id != getIssueResolution()?.id) {
            addToResolutionTimelines(new IssueResolutionTimeline(resolution: issueResolution))
        }
    }

    public IssueSeverity getIssueSeverity() {
        return !severityTimelines.empty ? severityTimelines.last().severity : null
    }

    public void setIssueSeverity(IssueSeverity issueSeverity) {
        if (issueSeverity?.id != getIssueSeverity()?.id) {
            addToSeverityTimelines(new IssueSeverityTimeline(severity: issueSeverity))
        }
    }

    public String getDescription() {
        return !descriptionTimelines.empty ? descriptionTimelines.last().description : null
    }

    public void setDescription(String description) {
        if (description != getDescription()) {
            addToDescriptionTimelines(new IssueDescriptionTimeline(description: description))
        }
    }

    public User getAssignee() {
        return !assigneeTimelines.empty ? assigneeTimelines.last().assignee : null
    }

    public void setAssignee(User assignee) {
        if (assignee?.id != getAssignee()?.id) {
            addToAssigneeTimelines(new IssueAssigneeTimeline(assignee: assignee))
        }
    }

    def beforeInsert = {
        //issues are always created as open
        setIssueStatus(IssueStatus.OPEN)

        number = project.nextIssueNumber
        project.nextIssueNumber++
        project.save()
    }

    public String toString() {
        return "${project.code}-$number"
    }

    public int compareTo(Object o) {
        return project.code.compareTo(o.project.code) ?: number.compareTo(o.number)
    }
}
